/**
  frame-buffer.c - Created by Timothy Morey on 5/17/2012
*/

#include <stdlib.h>
#include <string.h>

#include "err.h"
#include "frame-buffer.h"
#include "mutex.h"

int AddFrameToBuffer(FrameBuffer* buf, const char* frame, int frameSize, int* frameindex)
{
  int retval = NVN_NOERR;

  if(buf && frame && frameSize > 0)
  {
    int i = 0;

    GetLock(buf->Mutex);

    if(buf->LastFrame >= 0)
      GetNextIndex(buf, buf->LastFrame, &i);

    if(buf->Frames[i].Buffer && frameSize > buf->Frames[i].Length)
      free(buf->Frames[i].Buffer);
    
    if(! buf->Frames[i].Buffer)
      buf->Frames[i].Buffer = (char*)malloc(frameSize);

    buf->Frames[i].Length = frameSize;
    memcpy(buf->Frames[i].Buffer, frame, frameSize);
    buf->LastFrame = i;

    if(buf->FirstFrame < 0)
      buf->FirstFrame = i;
    else if(buf->FirstFrame == i)
      GetNextIndex(buf, buf->FirstFrame, &buf->FirstFrame);

    if(frameindex)
      *frameindex = i;

    ReleaseLock(buf->Mutex);
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}

int DestroyFrameBuffer(FrameBuffer* buf)
{
  int retval = NVN_NOERR;

  if(buf)
  {
    int i = 0;

    DestroyMutex(&buf->Mutex);
    for(i = 0; i < buf->NFrames; i++)
    {
      if(buf->Frames[i].Buffer)
        free(buf->Frames[i].Buffer);
    }

    if(buf->Frames)
      free(buf->Frames);
  }

  return retval;
}

int ExpandFrameBuffer(FrameBuffer* buf, int n)
{
  int retval = NVN_NOERR;

  if(buf && n > buf->NFrames)
  {
    Frame* temp = (Frame*)realloc(buf->Frames, n * sizeof(Frame));
    if(temp)
    {
      buf->Frames = temp;
      buf->NFrames = n;
    }
  }

  return retval;
}

int GetFrameCount(FrameBuffer* buf, int* count)
{
  int retval = NVN_NOERR;
  int valid = 0;

  if(buf && count)
  {
    GetLock(buf->Mutex);

    if(NVN_NOERR == IsValidIndex(buf, buf->FirstFrame, &valid) && valid && 
       NVN_NOERR == IsValidIndex(buf, buf->LastFrame, &valid) && valid)
    {
      if(buf->FirstFrame <= buf->LastFrame)
        *count = buf->LastFrame - buf->FirstFrame + 1;
      else
        *count = buf->NFrames - (buf->LastFrame - buf->FirstFrame - 1);
    }
    else
    {
      *count = 0;
    }

		ReleaseLock(buf->Mutex);
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}

int GetNextIndex(FrameBuffer* buf, int i, int* nexti)
{
  int retval = NVN_NOERR;

  if(buf && nexti)
  {
    *nexti = (i + 1) % buf->NFrames;
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}

int GetPrevIndex(FrameBuffer* buf, int i, int* previ)
{
  int retval = NVN_NOERR;

  if(buf && previ)
  {
    *previ = i - 1;

    if(*previ < 0)
      *previ = buf->NFrames - 1;
  }

  return retval;
}

int InitFrameBuffer(FrameBuffer* buf, int n)
{
  int retval = NVN_NOERR;

  if(buf)
  {
    memset(buf, 0, sizeof(FrameBuffer));
    InitMutex(&buf->Mutex);
    buf->FirstFrame = -1;
    buf->LastFrame = -1;

    if(n > 0)
    {
      buf->Frames = (Frame*)malloc(n * sizeof(Frame));
      buf->NFrames = n;
      memset(buf->Frames, 0, n * sizeof(Frame));
    }
  }

  return retval;
}

int IsEmpty(FrameBuffer* buf, int* empty)
{
  int retval = NVN_NOERR;
  int valid = 0;

  if(buf && empty)
  {
    if(NVN_NOERR == IsValidIndex(buf, buf->FirstFrame, &valid) && valid && 
       NVN_NOERR == IsValidIndex(buf, buf->LastFrame, &valid) && valid)
    {
      *empty = 0;
    }
    else
    {
      *empty = 1;
    }
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}

int IsValidIndex(FrameBuffer* buf, int i, int* valid)
{
  int retval = NVN_NOERR;
  
  if(buf && valid)
  {
    if(buf->FirstFrame >= 0 && buf->LastFrame >= 0)
    {
      if(buf->FirstFrame > buf->LastFrame)
        *valid = (i >= buf->FirstFrame && i >= buf->LastFrame) ||
                 (i <= buf->FirstFrame && i <= buf->LastFrame);
      else
        *valid = i >= buf->FirstFrame && i <= buf->LastFrame;
    }
    else
    {
      *valid = 0;
    }
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}

int RemoveFrame(FrameBuffer* buf, int frameIndex)
{
  int retval = NVN_NOERR;
  int valid = 0;

  if(buf && 
     NVN_NOERR == IsValidIndex(buf, frameIndex, &valid) && valid &&
     (frameIndex == buf->FirstFrame || frameIndex == buf->LastFrame))
  {
    GetLock(buf->Mutex);

    if(buf->Frames[frameIndex].Buffer)
    {
      free(buf->Frames[frameIndex].Buffer);
      buf->Frames[frameIndex].Buffer = 0;
    }

    buf->Frames[frameIndex].Length = 0;

    if(frameIndex == buf->FirstFrame && frameIndex == buf->LastFrame)
    {
      buf->FirstFrame = -1;
      buf->LastFrame = -1;
    }
    else if(frameIndex == buf->FirstFrame)
    {
      GetNextIndex(buf, buf->FirstFrame, &buf->FirstFrame);
    }
    else if(frameIndex == buf->LastFrame)
    {
      GetPrevIndex(buf, buf->LastFrame, &buf->LastFrame);
    }

    ReleaseLock(buf->Mutex);
  }
  else
  {
    retval = NVN_EINVARGS;
  }

  return retval;
}
